import React, { CSSProperties } from 'react';
import { Empty, Spin } from 'antd';
import { SpinProps } from 'antd/es/spin';
import lodash from 'lodash';
import classNames from 'classnames';
import { getMapperDataLabel, getServerData, MapperData, ResponsiveBreakpoint, responsiveObserve, ServerDataConfig, TypeEnum, variableTypeOf } from '@/common';
import styles from './DetailTable.less';

/** 表格数据类型 */
interface DetailTableData {
  [key: string]: any;
}

/** 自定义Label内容 */
type Label = React.ReactNode | ((value: any, key: string, data: DetailTableData) => React.ReactNode);

/** Label单元格配置 */
interface LabelConfig {
  /** 当前列表头内容 */
  label: Label;
  /** 超过宽度将自动省略，设置为 true 时，表格布局将变成 tableLayout="fixed" */
  ellipsis?: boolean;
  /** 当前列自定义表头样式 */
  style?: CSSProperties;
  /** 当前列自定义表头class样式 */
  className?: string;
  /** 是否隐藏当前列 */
  hidden?: boolean;
}

/** 自定义value数据转换 */
type Transform = MapperData | ((value: any, key: string, data: DetailTableData) => React.ReactNode);

/** Value单元格配置 */
interface TransformConfig {
  /** 当前列表格内容 */
  transform?: Transform;
  /** 超过宽度将自动省略，设置为 true 时，表格布局将变成 tableLayout="fixed" */
  ellipsis?: boolean;
  /** 当前列自定义表格样式 */
  style?: CSSProperties;
  /** 当前列自定义表格class样式 */
  className?: string;
  /** 当前列表格列数(夸多列使用) */
  columnCount?: number;
  /** 是否隐藏当前列表格内容 */
  hidden?: boolean;
}

/** 宽度配置 */
interface WidthSpan {
  /** 宽度单位类型 */
  type: 'px' | '%' | 'vw' | 'em' | 'rem';
  /** 宽度值 */
  width: number;
}

/** 表格数据项 */
interface DetailTableItem {
  /** label 内容 */
  label: React.ReactNode;
  /** 超过宽度将自动省略，设置为 true 时，表格布局将变成 tableLayout="fixed" */
  ellipsis?: boolean;
  /** 当前列自定义表头样式 */
  style?: CSSProperties;
  /** 当前列自定义表头class样式 */
  className?: string;
  /** value 内容 */
  valueContent: {
    /** value 值 */
    value: React.ReactNode;
    /** 当前列表格列数(夸多列使用) */
    columnCount: number;
    /** 超过宽度将自动省略，设置为 true 时，表格布局将变成 tableLayout="fixed" */
    ellipsis?: boolean;
    /** 当前列自定义表格样式 */
    style?: CSSProperties;
    /** 当前列自定义表格class样式 */
    className?: string;
    /** 是否隐藏当前列表格内容 */
    hidden?: boolean;
  };
}

interface DetailTableProps {
  // ----------------------------------------------------------------------------------- 初始值
  /** 初始状态是否显示 */
  initShow?: boolean;
  /** 详情表格数据 */
  initData?: DetailTableData;
  // ----------------------------------------------------------------------------------- 核心配置
  /** 是否启用 ellipsis 配置功能，启用就设置 table-layout: fixed; */
  enableEllipsis?: boolean;
  /** 详情表格头部 */
  header?: React.ReactNode;
  /** 详情表格尾部 */
  footer?: React.ReactNode;
  /** 详情表格列数 */
  columnCount: number | Partial<Record<ResponsiveBreakpoint, number>>;
  /** label配置(决定显示字段和排序) */
  labels: { [key: string]: Label | LabelConfig };
  /** 数据转换配置 */
  dataTransforms?: { [key: string]: Transform | TransformConfig };
  /** label单元格宽度 */
  labelSpan: WidthSpan;
  /** value单元格宽度 */
  valueSpan?: WidthSpan;
  /** 统一的表头后缀 */
  labelSuffix?: React.ReactNode;
  /** 获取服务端数据配置 */
  serverData?: ServerDataConfig<DetailTableData>;
  /** loading变化事件 */
  onLoadingChange?: (loading: boolean) => void;
  // ----------------------------------------------------------------------------------- 扩展配置
  /** 边框颜色 | "#e8e8e8" | "#d8ecfc" */
  borderColor?: string;
  /** 表头单元格背景颜色 | "#fafafa" | "#e7f1fa" */
  labelBackgroundColor?: string;
  /** 当前组件自定义样式 */
  style?: CSSProperties;
  /** 当前组件自定义class样式 */
  className?: string;
  /** 详情表格头部自定义样式 */
  headerStyle?: CSSProperties;
  /** 详情表格头部自定义class样式 */
  headerClassName?: string;
  /** 详情表格尾部自定义样式 */
  footerStyle?: CSSProperties;
  /** 详情表格尾部自定义class样式 */
  footerClassName?: string;
  /** table自定义样式 */
  tableStyle?: CSSProperties;
  /** table自定义样式 */
  tableClassName?: string;
  /** 表格body自定义样式 */
  tbodyStyle?: CSSProperties;
  /** 表格body自定义class样式 */
  tbodyClassName?: string;
  /** tr行自定义样式 */
  trStyle?: CSSProperties;
  /** tr行自定义class样式 */
  trClassName?: string;
  /** th表头自定义样式 */
  thStyle?: CSSProperties;
  /** th表头自定义class样式 */
  thClassName?: string;
  /** td单元格自定义样式 */
  tdStyle?: CSSProperties;
  /** td单元格自定义class样式 */
  tdClassName?: string;
  /** 是否禁用Spin组件(不使用加载状态) */
  disableSpin?: boolean;
  /** Spin自定义属性 */
  spinProps?: SpinProps;
}

interface DetailTableState {
  /** 组件是否显示 */
  show: boolean;
  /** 表格数据 */
  data: DetailTableData;
  /** 表格列数 */
  columnCount: number;
  /** 是否是加载中状态 */
  loading: boolean;
}

class DetailTable extends React.Component<DetailTableProps, DetailTableState> {
  static defaultProps: Readonly<Partial<DetailTableProps>> = {
    initShow: true,
    initData: {},
    enableEllipsis: false,
    columnCount: 3,
    labels: {},
    labelSpan: { type: 'px', width: 210 },
    borderColor: '#f0f0f0',
    labelBackgroundColor: '#fafafa',
    disableSpin: false,
    spinProps: {
      delay: 200,
      spinning: false,
    },
  };

  // 订阅响应式断点的 Token
  protected breakpointSubscribeToken: number | undefined;

  // 组件是否已经挂载
  protected isDidMount: boolean = false;

  constructor(props: DetailTableProps) {
    super(props);
    const { initShow = true, initData = {}, columnCount } = props;
    this.state = {
      loading: false,
      show: initShow,
      data: initData,
      columnCount: variableTypeOf(columnCount) === TypeEnum.number ? (columnCount as number) : 3,
    };
    this.subscribeBreakpoint();
  }

  async componentDidMount() {
    this.isDidMount = true;
    const { serverData } = this.props;
    if (serverData && serverData.initLoadData) {
      await this.refresh();
    }
  }

  componentWillUnmount() {
    const { breakpointSubscribeToken } = this;
    if (breakpointSubscribeToken) {
      responsiveObserve.unsubscribe(breakpointSubscribeToken);
    }
  }

  /** 监听响应式断点 */
  protected subscribeBreakpoint() {
    const { columnCount } = this.props;
    if (variableTypeOf(columnCount) === TypeEnum.number || variableTypeOf(columnCount) !== TypeEnum.object) {
      return;
    }
    const breakpointMap: Partial<Record<ResponsiveBreakpoint, number>> = columnCount as Partial<Record<ResponsiveBreakpoint, number>>;
    this.breakpointSubscribeToken = responsiveObserve.subscribe((screens) => {
      let max = 3;
      lodash.forEach(screens, (match, key) => {
        if (!match || !breakpointMap[key]) return;
        max = breakpointMap[key];
      });
      if (this.isDidMount) {
        this.setColumnCount(max);
      } else {
        // @ts-ignore
        // noinspection JSConstantReassignment
        this.state.columnCount = max;
      }
    });
  }

  /** 详情表格头部标题 */
  protected getHeader(): React.ReactNode {
    const { header, headerStyle, headerClassName } = this.props;
    if (!header) return null;
    const border = this.getBorderStyle();
    const borderStyle: CSSProperties = { borderLeft: border, borderRight: border, borderTop: border };
    return (
      <div style={{ ...borderStyle, ...headerStyle }} className={classNames(styles.header, headerClassName)}>
        {header}
      </div>
    );
  }

  /** 详情表格 */
  protected getTable(): React.ReactNode {
    const labelArray: DetailTableItem[] = this.getLabels();
    if (labelArray.length <= 0) {
      return <Empty />;
    }
    const { enableEllipsis, labelSpan, valueSpan, labelSuffix, tableStyle, tableClassName, tbodyStyle } = this.props;
    const { tbodyClassName, trStyle, trClassName, thStyle, thClassName, tdStyle, tdClassName } = this.props;
    const border = this.getBorderStyle();
    const columnCount = this.getColumnCount();
    const tableRowArray: Array<React.ReactNode[]> = [];
    let tableRow: React.ReactNode[];
    // let rowCount: number = 0;
    let columnIndex: number = 0;
    let index = 0;
    lodash.forEach(labelArray, (labelItem) => {
      index++;
      // 第一行
      if (!tableRow) {
        tableRow = [];
        tableRowArray.push(tableRow);
      }
      // 判断是否需要换行
      if (columnIndex >= columnCount) {
        columnIndex = 0;
        // rowCount++;
        tableRow = [];
        tableRowArray.push(tableRow);
      }
      // 当前行剩下的列数
      const leftOverColumnCount = columnCount - columnIndex;
      // 下一条数据需要占用的列数
      let nextColumnCount = index >= labelArray.length ? 0 : labelArray[index].valueContent.columnCount;
      if (nextColumnCount > columnCount) {
        nextColumnCount = columnCount;
      }
      // 当前数据需要占用的列数
      // console.log("---> ", labelItem);
      let currentColumnCount = labelItem.valueContent.columnCount;
      if (currentColumnCount + nextColumnCount > leftOverColumnCount || nextColumnCount <= 0) {
        currentColumnCount = leftOverColumnCount;
      }
      // 增加列索引
      columnIndex += currentColumnCount;
      // th - label
      const baseThStyle: CSSProperties = { width: `${labelSpan.width}${labelSpan.type}`, backgroundColor: this.getLabelBackgroundColor(), borderRight: border };
      tableRow.push(
        <th
          key={`td-${index}`}
          style={{ ...baseThStyle, ...thStyle, ...labelItem.style }}
          className={classNames({ [styles.ellipsis]: enableEllipsis && labelItem.ellipsis }, thClassName, labelItem.className)}
          colSpan={1}
        >
          {labelItem.label}
          {labelSuffix}
        </th>,
      );
      // td - value
      const baseTdStyle: CSSProperties = {};
      if (valueSpan) baseTdStyle.width = `${valueSpan.width}${valueSpan.type}`;
      if (columnIndex < columnCount) baseTdStyle.borderRight = border;
      tableRow.push(
        <td
          key={`th-${index}`}
          style={{ ...baseTdStyle, ...tdStyle, ...labelItem.valueContent.style }}
          className={classNames({ [styles.ellipsis]: enableEllipsis && labelItem.valueContent.ellipsis }, tdClassName, labelItem.valueContent.className)}
          colSpan={currentColumnCount * 2 - 1}
        >
          {labelItem.valueContent.hidden ? null : labelItem.valueContent.value}
        </td>,
      );
    });

    const tableBaseStyle: CSSProperties = { border };
    if (enableEllipsis) {
      tableBaseStyle.tableLayout = 'fixed';
    }
    const trBorder: CSSProperties = { borderBottom: border };
    return (
      <table style={{ ...tableBaseStyle, ...tableStyle }} className={classNames(styles.table, tableClassName)}>
        <tbody style={{ ...tbodyStyle }} className={classNames(tbodyClassName)}>
          {tableRowArray.map((trContent, idx) => (
            <tr key={`tr-${idx}`} style={{ ...(idx + 1 < tableRowArray.length ? trBorder : {}), ...trStyle }} className={classNames(trClassName)}>
              {trContent}
            </tr>
          ))}
        </tbody>
      </table>
    );
  }

  protected getLabels(): DetailTableItem[] {
    const { labels, dataTransforms } = this.props;
    const data = this.getData();
    const labelArray: DetailTableItem[] = [];
    if (labels) {
      lodash.forEach(labels, (value, key) => {
        const dataValue = data ? data[key] : null;
        const labelItem: DetailTableItem = { label: null, ellipsis: true, valueContent: { value: dataValue, columnCount: 1, ellipsis: false } };
        if (value instanceof Function) {
          // Function
          labelItem.label = value(dataValue, key, data);
        } else if (value && (value as LabelConfig).label) {
          // LabelConfig
          const labelConfig: LabelConfig = value as LabelConfig;
          if (labelConfig.label instanceof Function) {
            labelItem.label = labelConfig.label(dataValue, key, data);
          } else if (labelConfig.label) {
            labelItem.label = labelConfig.label;
          }
          labelItem.ellipsis = labelConfig.ellipsis;
          labelItem.style = labelConfig.style;
          labelItem.className = labelConfig.className;
          // 是否隐藏了
          if (labelConfig.hidden) return;
        } else if (value) {
          // React.ReactNode
          labelItem.label = value;
        } else {
          // 未知
          return;
        }
        // dataTransforms 处理
        if (dataTransforms && dataTransforms[key]) {
          const transform = dataTransforms[key];
          if (transform instanceof Function) {
            // Function
            labelItem.valueContent.value = transform(dataValue, key, data);
          } else if (variableTypeOf(transform) === TypeEnum.object) {
            // TransformConfig
            const transformConfig: TransformConfig = transform as TransformConfig;
            if (transformConfig.transform instanceof Function) {
              labelItem.valueContent.value = transformConfig.transform(dataValue, key, data);
            } else if (transformConfig.transform) {
              labelItem.valueContent.value = getMapperDataLabel(transformConfig.transform, dataValue);
            }
            labelItem.valueContent.ellipsis = transformConfig.ellipsis;
            labelItem.valueContent.style = transformConfig.style;
            labelItem.valueContent.className = transformConfig.className;
            if (transformConfig.columnCount && transformConfig.columnCount >= 1) {
              labelItem.valueContent.columnCount = transformConfig.columnCount;
            }
            labelItem.valueContent.hidden = transformConfig.hidden;
          } else if (variableTypeOf(transform) === TypeEnum.array) {
            // MapperData
            labelItem.valueContent.value = getMapperDataLabel(transform, dataValue);
          }
        }
        labelArray.push(labelItem);
      });
    } else if (data) {
      lodash.forEach(data, (value, key) => {
        labelArray.push({ label: key, valueContent: { value, columnCount: 1 } });
      });
    }
    return labelArray;
  }

  /** 详情表格尾部 */
  protected getFooter(): React.ReactNode {
    const { footer, footerStyle, footerClassName } = this.props;
    if (!footer) return null;
    const border = this.getBorderStyle();
    const borderStyle: CSSProperties = { borderLeft: border, borderRight: border, borderBottom: border };
    return (
      <div style={{ ...borderStyle, ...footerStyle }} className={classNames(styles.footer, footerClassName)}>
        {footer}
      </div>
    );
  }

  /** 获取边框配置 */
  protected getBorderStyle(): string {
    const { borderColor } = this.props;
    return `1px solid ${borderColor ?? '#f0f0f0'}`;
  }

  /** 获取Label北京颜色 */
  protected getLabelBackgroundColor(): string {
    const { labelBackgroundColor } = this.props;
    return labelBackgroundColor ?? '#fafafa';
  }

  public render(): React.ReactNode {
    const { show, loading } = this.state;
    const { style, className, disableSpin, spinProps } = this.props;
    const WrapperComponent = disableSpin ? React.Fragment : Spin;
    const wrapperComponentProps: SpinProps = !disableSpin && spinProps ? spinProps : {};
    if (!disableSpin) wrapperComponentProps.spinning = loading;
    const baseStyle: CSSProperties = {};
    if (!show) baseStyle.display = 'none';
    return (
      <div style={{ ...style, ...baseStyle }} className={classNames(className)}>
        <WrapperComponent {...wrapperComponentProps}>
          {/* 详情表格头部 */}
          {this.getHeader()}
          {/* 详情表格 */}
          {this.getTable()}
          {/* 详情表格尾部 */}
          {this.getFooter()}
        </WrapperComponent>
      </div>
    );
  }

  // -------------------------------------------------------------------------------------------------------------- 外部方法

  /**
   * 当前是否是正在加载中
   */
  public isLoading(): boolean {
    const { loading } = this.state;
    return loading;
  }

  /**
   * 设置当前组件加载中状态
   */
  public setLoading(loading: boolean): void {
    const { loading: oldLoading } = this.state;
    const { onLoadingChange } = this.props;
    if (oldLoading !== loading && onLoadingChange instanceof Function) {
      onLoadingChange(loading);
    }
    this.setState({ loading });
  }

  /**
   * 获取当前数据
   */
  public getData(): DetailTableData {
    const { data } = this.state;
    return data;
  }

  /**
   * 设置显示的数据
   * @param data DetailTableData数据
   */
  public setData(data: DetailTableData): void {
    this.setState({ data });
  }

  /**
   * 当前组件是否隐藏
   */
  public isShow(): boolean {
    const { show } = this.state;
    return show;
  }

  /**
   * 设置是否隐藏组件
   * @param show false=(display: none) | true=(display: unset)
   */
  public setShow(show: boolean): void {
    this.setState({ show });
  }

  /**
   * 重新加载数据(刷新数据)
   */
  public async refresh(): Promise<void> {
    const { serverData } = this.props;
    if (serverData) {
      // 获取服务端数据
      this.setLoading(true);
      await getServerData(serverData, (data) => {
        this.setState({ data: data ?? {} });
        this.setLoading(false);
      });
    }
  }

  /**
   * 获取详情表格列数
   */
  public getColumnCount(): number {
    const { columnCount } = this.state;
    return columnCount;
  }

  /**
   * 设置详情表格列数
   */
  public setColumnCount(columnCount: number): void {
    if (columnCount && columnCount > 0) {
      this.setState({ columnCount });
    }
  }
}

export { DetailTableData, LabelConfig, TransformConfig, WidthSpan, DetailTableItem, DetailTableProps, DetailTableState, DetailTable };
